/**
 * Copyright (c) 2018-2022, NXOS Development Team
 * SPDX-License-Identifier: Apache-2.0
 * 
 * Contains: stack backtrace for x86 arch
 * 
 * Change Logs:
 * Date           Author            Notes
 * 2022-07-27     JasonHu           Init
 */

#include <base/backtrace.h>
#define NX_LOG_NAME "x86:backtrace"
#include <base/log.h>

/**
 * function call stack:
 * 
 * High address
 * +------------+
 * |paramN      |
 * |...         |
 * |param3      |
 * |param2      |
 * |param1      |
 * +------------+
 * |eip         |
 * |ebp         |
 * +------------+
 * |local var1  |
 * |local var2  |
 * |local var3  |
 * |...         |
 * |local varN  |
 * +-------------+
 * Lower address
 */
NX_PRIVATE NX_Size NX_HalStackBacktrace(void ** buffer, NX_Size size, NX_Addr fp, NX_Addr stackTop)
{
    int i = 0;
    NX_Size n = 0;
    NX_U32 ebp;
    NX_U32 eip = 0;

    ebp = fp; /* ebp is stack frame pointer */

    if (!ebp)
    {
        NX_CASM ("movl %%ebp, %0" :"=g" (ebp)::"memory"); /* get current ebp */
    }

    for(i = 0; i < size && ebp; i++)
    {
        /* check ebp under stack top */
        if (stackTop && ebp >= stackTop)
        {
            break;
        }

        eip = (NX_U32)((NX_U32 *)ebp + 1); /* eip above ebp */
        eip = *(NX_U32 *)eip; /* in x86, eip was caller's next ins. */
        ebp = *(NX_U32 *)ebp;
        buffer[i] = (void *)eip; /* save call addr */
        n++;
        
        /* check eip in kernel code range */
        if (eip < NX_KernelGetSegmentStart("text") || eip >= NX_KernelGetSegmentEnd("bss"))
        {
            break;
        }
    }
    return n;
}

NX_INTERFACE struct NX_StackBacktraceOps gStackBacktraceOpsInterface = 
{
    .backtrace      = NX_HalStackBacktrace,
};
